<?php

/**
 * User: day
 * Date: 2018/08/07
 * Time: 23:04
 * Desc:
 * 数字类型一定要 round 否则会有浮点误差
 * 新加 API 的时候 LoadOrderReport 要验证通过能统计注单才行
 * 金额的处理一定要加上 round 或者 intval
 * API 的拉取间隔说明：
 * 根据api的访问频率限制来定，如果没有则以5s为拉取间隔拉取
 */

namespace App\Console\Commands\ThirdOrder;

use App\Console\Base;
use App\Models\Order;
use App\Models\OrderRepairRecord;
use App\Services\ThirdAPI\GameApiService;
use common\enum\CommonEnum;
use common\utils\RedisKeys;
use App\Services\UserService;
use function GuzzleHttp\Psr7\str;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use common\utils\SysCode;

abstract class BaseOrderProcessor extends Base
{
    public $userInfos;

    const GAME_TYPE_SPORT = 'sport';
    const GAME_TYPE_LIVE = 'live';
    const GAME_TYPE_EGAME = 'egame';
    const GAME_TYPE_ESPORT = 'esport';//电竞

    // 注单类型 1:电子，2:视讯，3:体育 4:电竞 5:彩票 6:棋牌
    const ORDER_TYPE_EGAME = 1;
    const ORDER_TYPE_LIVE = 2;
    const ORDER_TYPE_SPORT = 3;
    const ORDER_TYPE_ESPORT = 4;
    const ORDER_TYPE_LOTTERY = 5;
    const ORDER_TYPE_CARD = 6;
    // 公共状态
    const COMMON_STATUS_INVALID = 0;
    const COMMON_STATUS_UNSETTLE = 1;
    const COMMON_STATUS_SETTLE = 2;

    // 0.id，会员用户名，会员ID，第三方用户名，注单ID，厅ID，游戏类型(体育，电子等)，代理ID，代理名称
    public $userName;
    public $userID;
    public $apiUserName;
    public $orderNumber;
    public $hallID;
    public $orderType;
    public $agentID;
    public $agentName;
    // 1.有效投注，投注额，输赢，派奖，贡献，奖金
    // 数字类型一定要 round 否则会有浮点误差, 加密校验也会失败
    public $validMoney;
    public $betMoney;
    public $winLossMoney;
    public $prizeMoney;
    public $contributeMoney;
    public $bonusMoney;
    public $result;
    // 2.公用状态，API 原始状态
    public $commonStatus;
    public $apiStatus;
    // 3.游戏ID, 游戏名称，注单内容，updateKey（可以根据这个字段重新抓取数据）,来源(1:pc, 2:h5, 3:移动端)
    public $gameCode;
    public $gameName;
    public $betContent;
    public $updateKey;
    public $platform;
    // 4.创建时间，更新时间，注单时间，结算时间，报表时间（和第三方对账）
    public $createTime;
    public $updateTime; // 数据库自动更新
    public $betTime;
    public $settleTime;
    public $reportTime;
    // 5.第三方 partner id，名字，对应第三方表id
    public $partnerID;
    public $partnerName;
    // 6.原始数据存底
    public $originData;
    public $latestUpdateKey;
    public $latestUpdateKeyId;
    /**
     * 初始化信息，子类需要重写这个方法
     */
    function init($argv = null)
    {
    }

    /**
     * 抓取主程序
     */
    public function handle()
    {
        // try {
        $this->init();
        $data = $this->catchData();
//             $data = <<<DATA
// {"agent_uid":235,"bullet_chips":5.1,"bullet_count":51,"end_chips":100003.3,"end_time":1535532659,"fish_dead_chips":2.9,"fish_price":0,"game_code":5001,"game_terminal":3,"id":"9103235132534785","jp_add":0.102,"nickname":"beta_fg_80_100059","scene_id":177720002,"start_chips":100005.5,"start_time":1535532644,"top_agent_uid":235,"total_agent_uid":235,"trans_chips":0,"type":1,"gt":"fish"}
// DATA;
//             $data = json_decode($data, true);
//             $data = array($data);
            // Log::info($data);exit;
            // todo 去重，SB，UG，BG，SG，CQ9
        $data = $this->removeRepeat($data);
        foreach ($data as $info) {
            $this->load($info);
            if ($this->loadUserInfo() === false) {
                //不存在用户，此处记录错误日志！
                Log::error("third order, user not found " . $this->signature, $info);
                //非正式环境， 过滤数据
                if (env('APP_ENV') != 'prod') {
                    continue;
                }
            }
            $this->saveOrUpdate();
        }
        $this->updateReport();
        if (!OrderStrategy::UpdateRepairTime ($this->partnerName,$this->latestUpdateKey,$this->latestUpdateKeyId)){
            $this->updateFlag($this->updateKey);
        }

        // } catch(\Exception $e) {
        //     $msg = "请修复程序再运行:\nMessage:" . $e->getMessage() . "\nFile:" . $e->getFile() . "\nLine:" . $e->getLine() . "\ntrace: " . $e->getTraceAsString() . PHP_EOL;
        //     $this->error($msg);
        // }
        $this->info("{$this->partnerName} 采集完成。");
    }

    public function loadUserInfo()
    {
        if (isset($this->userInfos[$this->apiUserName])) {
            $userInfo = $this->userInfos[$this->apiUserName];
        } else {
            $userInfo = UserService::getUserAgentInfoByApiName($this->apiUserName);
        }
        if (!$userInfo) {
            //异常用户数据
            $this->userInfos[$this->apiUserName] = [];
            $this->userID = 0;
            $this->userName = 'invalid_user';
            $this->agentID = 0;
            $this->agentName = '';
            return false;
        }
        $userInfo = (array)$userInfo;
        $this->userInfos[$this->apiUserName] = $userInfo;
        $this->userID = $userInfo['id'];
        $this->userName = $userInfo['username'];
        $this->agentID = $userInfo['agent_id'];
        $this->agentName = $userInfo['agent_name'];
    }

    /**
     * 保存数据或者更新数据
     */
    public function saveOrUpdate()
    {
        $this->hallID = $this->getHallIDFromName($this->apiUserName);
//        $this->hallID = 80;
        //判断第三方订单是否存在，需要新增一个表，和model
        $orderModel = Order::where(['order_number' => $this->orderNumber, 'partner_id' => $this->partnerID])->first();
        $ret = true;
        if ($orderModel) {
            $orderModel->bet_amount = $this->betMoney;
            $orderModel->validbet = $this->validMoney;
            $orderModel->prize = $this->prizeMoney;
            $orderModel->profit = $this->winLossMoney;
            $orderModel->status = $this->commonStatus;
            $orderModel->partner_status = (string)$this->apiStatus;
            $orderModel->game_code = (string)$this->gameCode;
            $orderModel->game_name = $this->gameName;
            $orderModel->game_type = $this->orderType;
            $orderModel->bet_content = $this->betContent;
            $orderModel->last_version_key = $this->updateKey;
            $orderModel->bet_time = $this->betTime;
            $orderModel->report_time = $this->reportTime;
            $orderModel->reckon_time = $this->settleTime;
            $orderModel->source = $this->originData;
            $orderModel->contribute_amount = $this->contributeMoney ?? 0;
            $orderModel->origin = $this->platform ?? CommonEnum::DEVICE_PC;
            $orderModel->bonus_amount = $this->bonusMoney ?? 0;
            $orderModel->sign = md5($this->userID . $this->userName . $this->partnerID . (int)$this->betMoney . (int)$this->validMoney .
                (int)$this->prizeMoney . (int)$this->winLossMoney . $this->orderNumber .
                (int)$this->contributeMoney . (int)$this->bonusMoney);
            $ret = $orderModel->save();
            $this->info("----- Update Data success: {$this->orderNumber}");
        } else {
            $orderModel = new Order();
            $orderModel->user_id = $this->userID;
            $orderModel->hall_id = $this->hallID;
            $orderModel->username = $this->userName;
            $orderModel->partner_account = $this->apiUserName;
            $orderModel->partner_id = $this->partnerID;
            $orderModel->partner_name = $this->partnerName;
            $orderModel->game_code = (string)$this->gameCode;
            $orderModel->game_name = $this->gameName;
            $orderModel->game_type = $this->orderType;
            $orderModel->order_number = $this->orderNumber;
            $orderModel->bet_amount = (int)$this->betMoney;
            $orderModel->validbet = (int)$this->validMoney;
            $orderModel->profit = (int)$this->winLossMoney;
            $orderModel->prize = (int)$this->prizeMoney;
            $orderModel->contribute_amount = (int)$this->contributeMoney;
            $orderModel->bonus_amount = (int)$this->bonusMoney;
            $orderModel->status = $this->commonStatus;
            $orderModel->partner_status = (string)$this->apiStatus;
            $orderModel->bet_time = $this->betTime;
            $orderModel->reckon_time = $this->settleTime;
            $orderModel->report_time = $this->reportTime;
            $orderModel->last_version_key = $this->updateKey;
            $orderModel->bet_content = $this->betContent;
            $orderModel->source = $this->originData;
            $orderModel->origin = $this->platform ?? CommonEnum::DEVICE_PC;
            $orderModel->sign = md5($this->userID . $this->userName . $this->partnerID . (int)$this->betMoney . (int)$this->validMoney .
                (int)$this->prizeMoney . (int)$this->winLossMoney . $this->orderNumber .
                (int)$this->contributeMoney . (int)$this->bonusMoney);
            $ret = $orderModel->save();
            $this->info("===== Save Data success: {$this->orderNumber}");
        }
        if (!$ret) {
            $msg = "{$this->partnerName} 保存注单失败，需要重新拉取, updateKey: {$this->updateKey}, time: {$this->betTime}, error:";
            $this->error($msg);
            return error_exit(SysCode::INTERNAL_SERVER_ERROR, $msg);
        }
    }

    /**
     * 向第三方API抓取数据
     */
    abstract function catchData();

    /**
     * 更新报表
     */
    public function updateReport()
    {
        // Redis::connection()->sAdd(RedisKeys::RESETTLE_TIME . $this->hallID, date('Y-m-d'));
    }
    /*
     * 更新拉取注单ID
     */
    abstract function updateFlag($updateKey);

    /**
     * 重数据库重新读取数据并加载
     */
    public function reloadFromSql()
    {
        // $data = Order::Where([])
    }

    /**
     * 处理数据
     */
    abstract function load($info);

    /**
     * 处理数据，每个子类需要根据第三方的单条数据内容来获取有用的信息
     */
    abstract function initFromData($data);

    /**
     * 修复数据内容，每个子类需要实现怎么修复数据内容
     */
    abstract function fixData($date);

    /**
     * 去除重复注单，UG，SB，BG 一次拉取会有重复注单出现，但是状态不变
     */
    function removeRepeat($data)
    {
        return $data;
    }

    /**
     * 根据用户名解析出厅ID
     * prod_fg_80_1234
     * prodXfgX80X1234
     * 
     */
    function getHallIDFromName($name)
    {
        $splits = ['_', 'X'];
        foreach ($splits as $s) {
            $arr = explode($s, strtoupper($name));
            if (count($arr) > 3) {
                return $arr[2];
            }
        }
        return 80;
    }
}
